package sg.edu.nus.iss.billing.validation;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import sg.edu.nus.iss.billing.exception.ValidationException;
import sg.edu.nus.iss.billing.localization.MessageKeyManager;
import sg.edu.nus.iss.billing.util.Util;

public class FormValidator {
	
	public static void validate(String fieldName, String value, ValidationRule ... rules) throws ValidationException {
		for (ValidationRule rule : rules) {
			switch(rule){
				case NOT_NULL : 
					if (Util.isEmpty(value)) 
							throw new ValidationException(MessageKeyManager.getMessage("err.null",fieldName));
					break;
					
				case ALPHANUMERIC :
					if(!isAlphaNumeric(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.alphaNumeric", fieldName));
					break;
					
				case NUMERIC :
					if(!isNumeric(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.numeric", fieldName));
					break;
					
				case DECIMAL :
					if(!isDecimal(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.decimal", fieldName));
					break;
					
				case LENGTH6:
					if(value.length() != 6)
						throw new ValidationException(MessageKeyManager.getMessage("err.length6",fieldName));
					break;
					
				case LENGTH8 :
					if(value.length() != 8)
						throw new ValidationException(MessageKeyManager.getMessage("err.length8",fieldName));
					break;
					
				case LENGTH9 :
					if(value.length() != 9)
						throw new ValidationException(MessageKeyManager.getMessage("err.length9",fieldName));
					break;
					
				case LENGTH10:
					if(value.length() != 10)
						throw new ValidationException(MessageKeyManager.getMessage("err.length10",fieldName));
					break;
					
				case LENGTH11:
					if(value.length() != 11)
						throw new ValidationException(MessageKeyManager.getMessage("err.length11",fieldName));
					break;
					
				case NRIC:
					if(!isValidNRIC(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.nric"));
					break;
					
				case DATE:
					if(!isDate(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.date", fieldName));
					break;
					
				case MONTHYEAR:
					if(!isMONTHYEAR(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.monthyear", fieldName));
					break;
					
				case GREATER_THAN_TODAY:
					if(!isGreaterThanToday(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.today", fieldName));
					break;
					
				case EMAIL:
					if(!isValidEmail(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.email"));
					break;
					
				case MOBILE:
					if(!isValidMobileNumber(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.phoneNumber", fieldName));
					break;
					
				case LAND_LINE:
					if(!isValidLandLine(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.phoneNumber", fieldName));
					break;
					
				case ACCOUNT_NUMBER:
					if(!isValidAccountNumber(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.accountNumber"));
					break;
					
				case COMPLAINT_ID:
					if(!isValidComplaintID(value))
						throw new ValidationException(MessageKeyManager.getMessage("err.complaintID"));
					break;
			}
		}
	}

	private static boolean isGreaterThanToday(String value) {
		Date date = null;
		if(Util.isEmpty(value) )
			return true;
		if(isDate(value)){
			date = Util.getDateFromString(value);
			Date curDate = Util.getDateFromString(Util.getStringFromdate(new Date()));
			if(date.before(curDate))
				return false;
			else
				return true;
		}
		return false;
	}

	private static boolean isMONTHYEAR(String value) {
		String expression = "((0?[1-9])|(1[012]))[- /](18|19|20|21)?\\d{2}$";
		CharSequence inputStr = value;
		Pattern pattern = Pattern.compile(expression);
		Matcher matcher = pattern.matcher(inputStr);
		if(Util.isEmpty(value) || matcher.matches())
			return true;
		else
			return false;
	}

	private static boolean isDecimal(String decimalVal) {
		String expression = "^-{0,1}\\d*\\.{0,1}\\d+$";
		CharSequence inputStr = decimalVal;
		Pattern pattern = Pattern.compile(expression);
		Matcher matcher = pattern.matcher(inputStr);
		if(Util.isEmpty(decimalVal) || matcher.matches())
			return true;
		else
			return false;
	}

	private static boolean isNumeric(String number) {
		String expression = "^\\d+$";
		CharSequence inputStr = number;
		Pattern pattern = Pattern.compile(expression);
		Matcher matcher = pattern.matcher(inputStr);
		if(Util.isEmpty(number) || matcher.matches())
			return true;
		else
			return false;
	}
	
	private static boolean isAlphaNumeric(String alphaNumeric) {
		if (Util.isEmpty(alphaNumeric) || alphaNumeric.matches("^[a-zA-Z0-9 ]+$"))
			return true;
		else
			return false;
	}
	
	private static boolean isAlphabet(String alphabet){
		if(Util.isEmpty(alphabet) || alphabet.matches("^[a-zA-Z ]+$"))
			return true;
		else
			return false;
	}
	
	private static boolean isAlphabet(char alphabet){
		return isAlphabet(Character.toString(alphabet));
	}
	
	private static boolean isValidNRIC(String value){
		char first, last;
		int checksum=0;
		char[] digit;
		char[] icLastLetter = new char[] {'J','Z','I','H','G','F','E','D','C','B','A'};
		char[] finLastLetter = new char[] {'X','W','U','T','R','Q','P','N','M','L','K'};
		
		if(value.length() != 9 || !isNumeric(value.substring(1,value.length()-1)) || !isAlphabet(value.charAt(0)) || !isAlphabet(value.charAt(8)))
			return false;
		
		first = value.charAt(0);
		last = value.charAt(8);
		digit = value.substring(1,value.length()-1).toCharArray();

		checksum = (Character.getNumericValue(digit[0]) * 2) + (Character.getNumericValue(digit[1]) * 7) + (Character.getNumericValue(digit[2]) * 6) + (Character.getNumericValue(digit[3]) * 5) + (Character.getNumericValue(digit[4]) * 4) + (Character.getNumericValue(digit[5]) * 3) + (Character.getNumericValue(digit[6]) * 2);

		if(first == 'T' || first == 'G')
			checksum += 4;
		
		checksum %= 11;
		
		if((first == 'S' || first == 'T') && icLastLetter[checksum] == last)
			return true;
		else if((first == 'F' || first == 'G') && finLastLetter[checksum] == last)
			return true;
		else
			return false;
	}
	
	private static boolean isDate(String value){
		if(Util.isEmpty(value) )
			return true;
		String dateFormat = MessageKeyManager.getMessage("ui.dateFormat");
		SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
		try {
			sdf.parse(value);
			return true;
		} catch (ParseException e) {}
		
		return false;
	}
	
	private static boolean isValidEmail(String email){
		String expression = "^[\\w\\.-]+@([\\w\\-]+\\.)+[A-Z]{2,4}$";
		CharSequence inputStr = email;
		Pattern pattern = Pattern.compile(expression,Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher(inputStr);
		if(Util.isEmpty(email) || matcher.matches())
			return true;
		else
			return false;
	}
	
	private static boolean isValidMobileNumber(String phoneNumber){
		String expression = "^[89]\\d{7}";
		CharSequence inputStr = phoneNumber;
		Pattern pattern = Pattern.compile(expression);
		Matcher matcher = pattern.matcher(inputStr);
		if(Util.isEmpty(phoneNumber) || matcher.matches())
			return true;
		else
			return false;
	}
	
	private static boolean isValidLandLine(String phoneNumber){
		String expression = "^[6]\\d{7}";
		CharSequence inputStr = phoneNumber;
		Pattern pattern = Pattern.compile(expression);
		Matcher matcher = pattern.matcher(inputStr);
		if(Util.isEmpty(phoneNumber) || matcher.matches())
			return true;
		else
			return false;
	}
	
	private static boolean isValidAccountNumber(String accountNumber){
		String expression = "1.1\\d{7}[A-Z]";
		CharSequence inputStr = accountNumber;
		Pattern pattern = Pattern.compile(expression);
		Matcher matcher = pattern.matcher(inputStr);
		if(Util.isEmpty(accountNumber) || matcher.matches())
			return true;
		else
			return false;
	}
	
	private static boolean isValidComplaintID(String complaintID){
		String expression = "C\\d{6}";
		CharSequence inputStr = complaintID;
		Pattern pattern = Pattern.compile(expression);
		Matcher matcher = pattern.matcher(inputStr);
		if(Util.isEmpty(complaintID) || matcher.matches())
			return true;
		else
			return false;
	}
}
